package com.gxc.core.validate.code;

import com.gxc.core.content.SessionContent;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

/**
 * @author GongXincheng
 * @since 2019-10-16 16:13
 */
@RestController
public class ValidateCodeController {

    static {
        System.setProperty("java.awt.headless", "true");
    }

    /**
     * Session 工具类
     */
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // 1：根据随机数生成图片
        ImageCode imageCode = createImageCode(request);

        // 2：将随机数存到Session中
        sessionStrategy.setAttribute(new ServletWebRequest(request), SessionContent.VALIDATE_SESSION_KEY, imageCode);

        // 3: 将生成的图片写到接口的相应中
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }

    /**
     * 根据随机数生成图片.
     */
    private ImageCode createImageCode(HttpServletRequest request) {
        // 干扰线
        final int count = 200;
        //定义图形大小(宽)
        final int width = 105;
        //定义图形大小(高)
        final int height = 35;
        //干扰线的长度=1.414*lineWidth
        final int lineWidth = 1;

        // 在内存中创建图象
        final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 获取图形上下文
        final Graphics2D graphics = (Graphics2D) image.getGraphics();

        // 设定背景颜色
        // ---1.Color.WHITE为白色
        graphics.setColor(Color.WHITE);
        // 填充衍射
        graphics.fillRect(0, 0, width, height);
        // 设定边框颜色
        // ---2.这是以数字型来设置颜色，颜色模式是指使用三种基色：红、绿、蓝，通过三种颜色的调整得出其它各种颜色，这三种基色的值范围为0～255
        graphics.drawRect(0, 0, width - 1, height - 1);

        final Random random = new Random();
        // 随机产生干扰线，使图象中的认证码不易被其它程序探测到
        for (int i = 0; i < count; i++) {
            // ---3.
            graphics.setColor(getRandColor(150, 200));
            // 保证画在边框之内
            final int x = random.nextInt(width - lineWidth - 1) + 1;
            final int y = random.nextInt(height - lineWidth - 1) + 1;
            final int xl = random.nextInt(lineWidth);
            final int yl = random.nextInt(lineWidth);
            graphics.drawLine(x, y, x + xl, y + yl);
        }

        // 取随机产生的认证码(4位数字)
        final String resultCode = exctractRandCode();
        for (int i = 0; i < resultCode.length(); i++) {
            // 设置字体颜色
            graphics.setColor(Color.BLACK);
            // 设置字体样式
            graphics.setFont(new Font("Times New Roman", Font.BOLD, 24));
            // 设置字符，字符间距，上边距
            System.out.print(resultCode.charAt(i));
            graphics.drawString(String.valueOf(resultCode.charAt(i)), (23 * i) + 8, 26);
        }
        // 图象生效
        graphics.dispose();

        System.out.println("直接输出：" + resultCode);
        return new ImageCode(image, resultCode, 3600L);
    }

    /**
     * 描述：根据给定的数字生成不同的颜色.
     *
     * @param fc 这是以数字型来设置颜色，颜色模式是指使用三种基色：红、绿、蓝，通过三种颜色的调整得出其它各种颜色，这三种基色的值范围为0～255
     * @param bc 这是以数字型来设置颜色，颜色模式是指使用三种基色：红、绿、蓝，通过三种颜色的调整得出其它各种颜色，这三种基色的值范围为0～255
     * @return 描述：返回颜色
     */
    private Color getRandColor(int fc, int bc) { // 取得给定范围随机颜色
        final Random random = new Random();
        if (fc > 255) {
            fc = 255;
        }
        if (bc > 255) {
            bc = 255;
        }

        final int r = fc + random.nextInt(bc - fc);
        final int g = fc + random.nextInt(bc - fc);
        final int b = fc + random.nextInt(bc - fc);

        return new Color(r, g, b);
    }

    /**
     * 随机码.
     */
    private String exctractRandCode() {
        final String randCodeType = ResourceUtil.getRandCodeType();
        int randCodeLength = Integer.parseInt(ResourceUtil.getRandCodeLength());
        if (randCodeType == null) {
            return RandCodeImageEnum.NUMBER_CHAR.generateStr(randCodeLength);
        } else {
            switch (randCodeType.charAt(0)) {
                case '1':
                    return RandCodeImageEnum.NUMBER_CHAR.generateStr(randCodeLength);
                case '2':
                    return RandCodeImageEnum.LOWER_CHAR.generateStr(randCodeLength);
                case '3':
                    return RandCodeImageEnum.UPPER_CHAR.generateStr(randCodeLength);
                case '4':
                    return RandCodeImageEnum.LETTER_CHAR.generateStr(randCodeLength);
                case '5':
                    return RandCodeImageEnum.ALL_CHAR.generateStr(randCodeLength);
                default:
                    return RandCodeImageEnum.NUMBER_CHAR.generateStr(randCodeLength);
            }
        }
    }

    /**
     * 验证码辅助类
     */
    enum RandCodeImageEnum {
        /**
         * 混合字符串 去除小写的l和o这个两个不容易区分的字符；
         */
        ALL_CHAR("0123456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"),
        /**
         * 字符
         */
        LETTER_CHAR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"),

        /**
         * 小写字母
         */
        LOWER_CHAR("abcdefghijklmnopqrstuvwxyz"),

        /**
         * 数字
         */
        NUMBER_CHAR("0123456789"),
        /**
         * 大写字符
         */
        UPPER_CHAR("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        /**
         * 待生成的字符串
         */
        private String charStr;

        /**
         * @param charStr
         */
        private RandCodeImageEnum(final String charStr) {
            this.charStr = charStr;
        }

        /**
         * 生产随机验证码
         *
         * @param codeLength 验证码的长度
         * @return 验证码
         */
        public String generateStr(final int codeLength) {
            final StringBuffer sb = new StringBuffer();
            final Random random = new Random();
            final String sourseStr = getCharStr();

            for (int i = 0; i < codeLength; i++) {
                sb.append(sourseStr.charAt(random.nextInt(sourseStr.length())));
            }

            return sb.toString();
        }

        /**
         * @return the {@link #charStr}
         */
        public String getCharStr() {
            return charStr;
        }
    }
}
